// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Uncle_the_shooter

//@version=6
indicator("Age Adaptive Supertrend", overlay = true, max_labels_count = 500, max_lines_count = 500)

// Inputs

const string G_BAND  = "Band"
const string G_ADPT  = "Adaptation"
const string G_LOOK  = "Style"
const string G_TPSL  = "TP/SL"
const string G_TPSLD = "TP/SL – Display"
const string G_TBL   = "Table"

atrLen    = input.int(10, "ATR Length", minval = 1, group = G_BAND,
     tooltip = "Averaging period of the Average True Range. Higher values smooth the band at the cost of slower reaction to changes in volatility.")
baseMult  = input.float(3.0, "Base Multiplier", minval = 0.1, step = 0.1, group = G_BAND,
     tooltip = "Distance of the band from hl2 in ATR multiples at the start of a trend (equivalent to the multiplier of a classic Supertrend). The same multiplier defines the reference Supertrend used to measure trend durations.")
kExp      = input.float(1.0, "Tightening Strength", minval = 0.0, step = 0.1, group = G_BAND,
     tooltip = "How strongly the band tightens as the trend ages relative to the historical distribution of trend durations. 0 – no tightening (classic Supertrend), 1 – standard, above 1 – aggressive tightening.")
floorMult = input.float(1.2, "Minimum Multiplier", minval = 0.1, step = 0.1, group = G_BAND,
     tooltip = "Lower limit of the ATR multiplier. Prevents the stop level from moving too close to price and closing positions prematurely due to market noise.")

halfLife  = input.float(40.0, "Half-Life (in trends)", minval = 1.0, step = 1.0, group = G_ADPT,
     tooltip = "Half-life of the weights in the trend-duration statistics, expressed in number of trends: a trend this many trends ago has a weight of 0.5. Lower values adapt faster to regime changes at the cost of less stable estimates.")
useEr     = input.bool(true, "Protect Efficient Trends", group = G_ADPT,
     tooltip = "Band tightening depends on the efficiency ratio (ER) of the current trend. With a high ER the band keeps its standard width, allowing positions to be held through strong directional moves; as ER falls, tightening increases to its full value. When disabled, tightening depends on trend age only.")
erHi      = input.float(0.40, "Efficiency Threshold", minval = 0.25, maxval = 1.0, step = 0.05, group = G_ADPT,
     tooltip = "Efficiency ratio measured from the start of the current trend on closing prices: price displacement in the trend direction divided by the sum of all bar-to-bar moves (0–1). Above the threshold there is no tightening; at a value 0.25 lower tightening is full; in between it is linear. ER = 1 means a one-way move without pullbacks; ER near 0 means consolidation or a move against the trend. ER is naturally high in young trends and naturally lower in long ones.")

upColor    = input.color(color.rgb(6, 162, 47),  "Up Color", group = G_LOOK, inline = "col")
downColor  = input.color(color.rgb(207, 23, 23), "Down",     group = G_LOOK, inline = "col",
     tooltip = "Colors of the line, gradient, bars and labels for uptrends and downtrends.")
lineTransp = input.int(40, "Line Transparency", minval = 0, maxval = 100, group = G_LOOK,
     tooltip = "0 – fully opaque, 100 – line hidden.")
fillTransp = input.int(80, "Gradient Transparency", minval = 0, maxval = 100, group = G_LOOK,
     tooltip = "Transparency of the fill next to the trend line. Toward price the fill fades by an additional 20 points. A value of 100 disables the gradient.")
colorBars  = input.bool(true, "Color Bars", group = G_LOOK,
     tooltip = "Colors bars according to the direction of the current trend.")
showSig    = input.bool(true, "Buy/Sell Labels", group = G_LOOK,
     tooltip = "Shows labels on bars where the trend direction changes. On the live bar a signal may appear and disappear before the bar closes; signals are final once the bar has closed.")
showRef    = input.bool(false, "Reference ST", group = G_LOOK, inline = "ref")
refColor   = input.color(color.new(color.gray, 60), "", group = G_LOOK, inline = "ref",
     tooltip = "Classic Supertrend with the same ATR length and base multiplier, used to build the trend-duration statistics. Shows how far the adaptive band deviates from the classic one.")

showTargets = input.bool(true, "Show TP/SL Levels", group = G_TPSL,
     tooltip = "Draws Entry, Stop Loss and Take Profit levels for the most recent Buy/Sell signal. Levels are extended until the Stop Loss or the farthest displayed Take Profit is reached, or until the next signal.")
useAtrSL    = input.bool(true, "SL = ATR", group = G_TPSL,
     tooltip = "Enabled: Stop Loss distance from entry is based on ATR (SL ATR Multiplier).\nDisabled: fixed percentage distance (SL % from Entry).")
tpAtrPeriod = input.int(14, "ATR Period (TP/SL)", minval = 1, group = G_TPSL,
     tooltip = "ATR period used to calculate the Stop Loss distance and Take Profit levels. Independent of the band ATR length.")
slAtrMult   = input.float(1.5, "SL ATR Multiplier", minval = 0.1, step = 0.1, group = G_TPSL,
     tooltip = "ATR multiplier defining the Stop Loss distance from entry; active when SL = ATR is enabled.")
slPercent   = input.float(1.0, "SL % from Entry", minval = 0.01, step = 0.1, group = G_TPSL,
     tooltip = "Percentage distance of the Stop Loss from entry; used when SL = ATR is disabled.")
rrTP1       = input.float(1.0, "TP1 Risk:Reward", minval = 0.1, step = 0.1, group = G_TPSL,
     tooltip = "Reward-to-risk ratio for TP1. A value of 1.0 places TP1 at a distance equal to the risk (SL) from entry.")
rrTP2       = input.float(2.0, "TP2 Risk:Reward", minval = 0.1, step = 0.1, group = G_TPSL,
     tooltip = "Reward-to-risk ratio for TP2.")
rrTP3       = input.float(3.0, "TP3 Risk:Reward", minval = 0.1, step = 0.1, group = G_TPSL,
     tooltip = "Reward-to-risk ratio for TP3.")

showSlLevel  = input.bool(true, "Show SL",  group = G_TPSLD, tooltip = "Draws the Stop Loss line and label for the active signal.")
showTp1Level = input.bool(true, "Show TP1", group = G_TPSLD, tooltip = "Draws the Take Profit 1 line and label for the active signal.")
showTp2Level = input.bool(true, "Show TP2", group = G_TPSLD, tooltip = "Draws the Take Profit 2 line and label for the active signal.")
showTp3Level = input.bool(true, "Show TP3", group = G_TPSLD, tooltip = "Draws the Take Profit 3 line and label for the active signal.")

showTable  = input.bool(true, "Show Table", group = G_TBL,
     tooltip = "Summary of the current trend: direction, age relative to history, move efficiency since the start of the trend (ER), current band multiplier and stop level. The multiplier sets how far from hl2 a new band level is calculated; the trend line never moves backward, so its actual distance from price (Stop row) may be smaller.")
tablePosIn = input.string("Bottom Right", "Position",
     options = ["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"],
     group = G_TBL, tooltip = "Position of the table on the chart.")
tableSizeIn = input.string("Normal", "Size",
     options = ["Tiny", "Small", "Normal", "Large", "Huge"],
     group = G_TBL, tooltip = "Text size in the table.")
tblBg      = input.color(#000000, "Background", group = G_TBL, inline = "tbl")
tblText    = input.color(color.white, "Text",   group = G_TBL, inline = "tbl",
     tooltip = "Background and text color of the table. For a light chart theme, a light background with dark text is recommended.")
tblTransp  = input.int(0, "Background Transparency", minval = 0, maxval = 100, group = G_TBL,
     tooltip = "0 – fully opaque background, 100 – background hidden.")

// Internal constants

const int   MAX_HIST    = 300    // maximum number of stored trends per direction
const int   MIN_SAMPLES = 15     // minimum number of historical trends before tightening is applied
const float SMOOTH_H    = 2.0    // smoothing width of the survival function (in bars)
const bool  USE_CENSOR  = true   // include the ongoing trend as a censored observation (Kaplan–Meier)
const bool  SPLIT_DIR   = true   // separate statistics for uptrends and downtrends

refMult = baseMult
erLo    = erHi - 0.25

// Helper functions

// Logistic function.
sig(float x) =>
    1.0 / (1.0 + math.exp(-x))

// Exponentially weighted, smoothed survival function at age t.
// Returns numerator and denominator [Σ w·a, Σ w] so that a censored observation can be added.
survAccum(array<int> durs, array<int> ids, int t, int nowId, float hl, float h) =>
    float num = 0.0
    float den = 0.0
    for [i, dv] in durs
        float w = math.pow(0.5, float(nowId - ids.get(i)) / hl)
        float a = h > 0 ? sig((dv - t - 0.5) / h) / sig((dv - 0.5) / h) : (dv > t ? 1.0 : 0.0)
        num += w * a
        den += w
    [num, den]

// Appends a trend duration to the history while keeping it within the size limit.
pushCapped(array<int> durs, array<int> ids, int v, int id, int cap) =>
    durs.push(v)
    ids.push(id)
    if durs.size() > cap
        durs.shift()
        ids.shift()

tablePosition(string s) =>
    switch s
        "Top Left"      => position.top_left
        "Top Center"    => position.top_center
        "Top Right"     => position.top_right
        "Middle Left"   => position.middle_left
        "Middle Center" => position.middle_center
        "Middle Right"  => position.middle_right
        "Bottom Left"   => position.bottom_left
        "Bottom Center" => position.bottom_center
        => position.bottom_right

tableTextSize(string s) =>
    switch s
        "Tiny"   => size.tiny
        "Small"  => size.small
        "Normal" => size.normal
        "Large"  => size.large
        => size.huge

// Reference Supertrend: trend-duration history

[refST, refDir] = ta.supertrend(refMult, atrLen)   // refDir < 0 means uptrend

var array<int> durUp = array.new<int>()
var array<int> idsUp = array.new<int>()
var array<int> durDn = array.new<int>()
var array<int> idsDn = array.new<int>()
var int refAge = 0
var int seqId  = 0

bool refFlip = not na(refDir[1]) and refDir != refDir[1]
if refFlip
    seqId += 1
    if refDir[1] < 0
        pushCapped(durUp, idsUp, refAge, seqId, MAX_HIST)
    else
        pushCapped(durDn, idsDn, refAge, seqId, MAX_HIST)
    refAge := 1
else
    refAge += 1

// Adaptive trend state

var int   dir    = 1
var int   age    = 0
var float upBand = na
var float dnBand = na
var float tStart = na
var float tPath  = 0.0

// Directional efficiency ratio since the start of the trend: movement against the trend yields 0.
if na(tStart)
    tStart := close
tPath += math.abs(close - nz(close[1], close))
float er = tPath > 0 ? math.max(0.0, dir * (close - tStart)) / tPath : 1.0

// Trend continuation probability S(t)

[numU, denU] = survAccum(durUp, idsUp, age, seqId + 1, halfLife, SMOOTH_H)
[numD, denD] = survAccum(durDn, idsDn, age, seqId + 1, halfLife, SMOOTH_H)

float num  = SPLIT_DIR ? (dir == 1 ? numU : numD) : numU + numD
float den  = SPLIT_DIR ? (dir == 1 ? denU : denD) : denU + denD
int   nObs = SPLIT_DIR ? (dir == 1 ? durUp.size() : durDn.size()) : durUp.size() + durDn.size()

// The ongoing reference trend is already longer than the current age: add it as a censored observation.
bool refIsUp    = refDir < 0
bool censorFits = not SPLIT_DIR or ((dir == 1) == refIsUp)
if USE_CENSOR and censorFits and refAge > age
    num += 1.0
    den += 1.0

float S = nObs >= MIN_SAMPLES and den > 0 ? num / den : 1.0

// Effective tightening factor and band multiplier.
float g    = useEr ? math.max(0.0, math.min(1.0, (erHi - er) / math.max(erHi - erLo, 1e-6))) : 1.0
float Seff = 1.0 - (1.0 - S) * g
float mult = math.max(floorMult, baseMult * math.pow(Seff, kExp))

// Bands and direction change

// Only the band of the active trend is tightened; the opposite band uses the base multiplier.
float multUp = dir == 1  ? mult : baseMult
float multDn = dir == -1 ? mult : baseMult

float atr   = ta.atr(atrLen)
float atrTP = ta.atr(tpAtrPeriod)
float newUp = hl2 - multUp * atr
float newDn = hl2 + multDn * atr

float prevUp = upBand
float prevDn = dnBand
upBand := na(prevUp) or close[1] < prevUp ? newUp : math.max(newUp, prevUp)
dnBand := na(prevDn) or close[1] > prevDn ? newDn : math.min(newDn, prevDn)

int prevDir = dir
if dir == 1 and close < upBand
    dir := -1
else if dir == -1 and close > dnBand
    dir := 1

bool flipped = dir != prevDir
if flipped
    age    := 1
    tStart := close
    tPath  := 0.0
else
    age += 1

bool buySignal  = flipped and dir == 1
bool sellSignal = flipped and dir == -1

// Visuals

float st       = dir == 1 ? upBand : dnBand
float plotLine = flipped ? na : st

color trendCol = dir == 1 ? upColor : downColor

pLine   = plot(plotLine, "Trend Line", color = color.new(trendCol, lineTransp), linewidth = 2, style = plot.style_linebr)
pCenter = plot(hl2, "hl2", display = display.none)

fill(pLine, pCenter, plotLine, hl2,
     color.new(trendCol, fillTransp),
     color.new(trendCol, math.min(100, fillTransp + 20)),
     title = "Trend Gradient")

plot(showRef ? refST : na, "Reference ST", color = refColor, style = plot.style_linebr)

if showSig and flipped
    label.new(bar_index,
         dir == 1 ? low - atr * 0.5 : high + atr * 0.5,
         dir == 1 ? "Buy" : "Sell",
         color     = color.new(trendCol, 40),
         style     = dir == 1 ? label.style_label_up : label.style_label_down,
         textcolor = color.white,
         size      = size.small)

barcolor(colorBars ? trendCol : na)

// TP/SL: state

var int      tpslDir        = 0
var float    tpslSL         = na
var float    tpslExtreme    = na
var line     tpslEntryLn    = na
var label    tpslEntryLb    = na
var line     tpslSlLn       = na
var label    tpslSlLb       = na
var line     tpslTp1Ln      = na
var label    tpslTp1Lb      = na
var line     tpslTp2Ln      = na
var label    tpslTp2Lb      = na
var line     tpslTp3Ln      = na
var label    tpslTp3Lb      = na
var line     tpslExtLn      = na
var linefill tpslRiskFill   = na
var linefill tpslRewardFill = na
var int      tpslEntryBar   = na

// TP/SL: levels for a new signal

if showTargets and (buySignal or sellSignal)
    bool isBuyTrade = buySignal

    // Remove the levels of the previous signal.
    line.delete(tpslEntryLn)
    label.delete(tpslEntryLb)
    line.delete(tpslSlLn)
    label.delete(tpslSlLb)
    line.delete(tpslTp1Ln)
    label.delete(tpslTp1Lb)
    line.delete(tpslTp2Ln)
    label.delete(tpslTp2Lb)
    line.delete(tpslTp3Ln)
    label.delete(tpslTp3Lb)
    line.delete(tpslExtLn)
    linefill.delete(tpslRiskFill)
    linefill.delete(tpslRewardFill)
    tpslEntryLn    := na
    tpslEntryLb    := na
    tpslSlLn       := na
    tpslSlLb       := na
    tpslTp1Ln      := na
    tpslTp1Lb      := na
    tpslTp2Ln      := na
    tpslTp2Lb      := na
    tpslTp3Ln      := na
    tpslTp3Lb      := na
    tpslExtLn      := na
    tpslRiskFill   := na
    tpslRewardFill := na

    tpslDir      := isBuyTrade ? 1 : -1
    tpslEntryBar := bar_index

    float entryP   = close
    float riskDist = useAtrSL ? atrTP * slAtrMult : entryP * (slPercent / 100.0)

    // Lines and labels: transparency 40; risk and reward zone fills: 80.
    color entryCol    = color.new(isBuyTrade ? upColor : downColor, 40)
    color slCol       = color.new(isBuyTrade ? downColor : upColor, 40)
    color tpCol       = color.new(isBuyTrade ? upColor : downColor, 40)
    color riskFillC   = color.new(isBuyTrade ? downColor : upColor, 80)
    color rewardFillC = color.new(isBuyTrade ? upColor : downColor, 80)

    float slP  = isBuyTrade ? entryP - riskDist : entryP + riskDist
    float tp1P = isBuyTrade ? entryP + riskDist * rrTP1 : entryP - riskDist * rrTP1
    float tp2P = isBuyTrade ? entryP + riskDist * rrTP2 : entryP - riskDist * rrTP2
    float tp3P = isBuyTrade ? entryP + riskDist * rrTP3 : entryP - riskDist * rrTP3

    // The farthest displayed TP ends the drawing of the levels.
    tpslSL      := slP
    tpslExtreme := showTp3Level ? tp3P : showTp2Level ? tp2P : showTp1Level ? tp1P : na

    tpslEntryLn := line.new(bar_index, entryP, bar_index + 1, entryP,
         color = entryCol, width = 2, extend = extend.none)
    tpslEntryLb := label.new(bar_index, entryP,
         (isBuyTrade ? "BUY " : "SELL ") + str.tostring(entryP, format.mintick),
         style = label.style_label_left, color = entryCol,
         textcolor = color.white, size = size.small)

    if showSlLevel
        tpslSlLn := line.new(bar_index, slP, bar_index + 1, slP,
             color = slCol, width = 1, style = line.style_dashed, extend = extend.none)
        tpslSlLb := label.new(bar_index, slP,
             "SL " + str.tostring(slP, format.mintick),
             style = label.style_label_left, color = slCol,
             textcolor = color.white, size = size.small)

    if showTp1Level
        tpslTp1Ln := line.new(bar_index, tp1P, bar_index + 1, tp1P,
             color = tpCol, width = 1, extend = extend.none)
        tpslTp1Lb := label.new(bar_index, tp1P,
             "TP1 " + str.tostring(tp1P, format.mintick),
             style = label.style_label_left, color = tpCol,
             textcolor = color.white, size = size.small)

    if showTp2Level
        tpslTp2Ln := line.new(bar_index, tp2P, bar_index + 1, tp2P,
             color = tpCol, width = 1, extend = extend.none)
        tpslTp2Lb := label.new(bar_index, tp2P,
             "TP2 " + str.tostring(tp2P, format.mintick),
             style = label.style_label_left, color = tpCol,
             textcolor = color.white, size = size.small)

    if showTp3Level
        tpslTp3Ln := line.new(bar_index, tp3P, bar_index + 1, tp3P,
             color = tpCol, width = 1, style = line.style_dotted, extend = extend.none)
        tpslTp3Lb := label.new(bar_index, tp3P,
             "TP3 " + str.tostring(tp3P, format.mintick),
             style = label.style_label_left, color = tpCol,
             textcolor = color.white, size = size.small)

    if showSlLevel
        tpslRiskFill := linefill.new(tpslEntryLn, tpslSlLn, riskFillC)

    // The reward zone extends to the farthest displayed TP.
    float fillExtreme = entryP
    if isBuyTrade
        if showTp1Level
            fillExtreme := math.max(fillExtreme, tp1P)
        if showTp2Level
            fillExtreme := math.max(fillExtreme, tp2P)
        if showTp3Level
            fillExtreme := math.max(fillExtreme, tp3P)
        if fillExtreme > entryP
            tpslExtLn      := line.new(bar_index, fillExtreme, bar_index + 1, fillExtreme, color = na, extend = extend.none)
            tpslRewardFill := linefill.new(tpslEntryLn, tpslExtLn, rewardFillC)
    else
        if showTp1Level
            fillExtreme := math.min(fillExtreme, tp1P)
        if showTp2Level
            fillExtreme := math.min(fillExtreme, tp2P)
        if showTp3Level
            fillExtreme := math.min(fillExtreme, tp3P)
        if fillExtreme < entryP
            tpslExtLn      := line.new(bar_index, fillExtreme, bar_index + 1, fillExtreme, color = na, extend = extend.none)
            tpslRewardFill := linefill.new(tpslEntryLn, tpslExtLn, rewardFillC)

// TP/SL: extending and closing the levels

if tpslDir != 0
    if not na(tpslEntryLb)
        label.set_x(tpslEntryLb, bar_index)
    if not na(tpslSlLb)
        label.set_x(tpslSlLb, bar_index)
    if not na(tpslTp1Lb)
        label.set_x(tpslTp1Lb, bar_index)
    if not na(tpslTp2Lb)
        label.set_x(tpslTp2Lb, bar_index)
    if not na(tpslTp3Lb)
        label.set_x(tpslTp3Lb, bar_index)

    if not na(tpslEntryLn)
        line.set_x2(tpslEntryLn, bar_index + 1)
    if not na(tpslSlLn)
        line.set_x2(tpslSlLn, bar_index + 1)
    if not na(tpslTp1Ln)
        line.set_x2(tpslTp1Ln, bar_index + 1)
    if not na(tpslTp2Ln)
        line.set_x2(tpslTp2Ln, bar_index + 1)
    if not na(tpslTp3Ln)
        line.set_x2(tpslTp3Ln, bar_index + 1)
    if not na(tpslExtLn)
        line.set_x2(tpslExtLn, bar_index + 1)

    bool slHit = tpslDir == 1 ? low <= tpslSL : high >= tpslSL
    bool tpHit = not na(tpslExtreme) and
         (tpslDir == 1 ? high >= tpslExtreme : low <= tpslExtreme)

    // Once SL or the farthest TP is reached, the levels end on the current bar.
    if bar_index > tpslEntryBar and (slHit or tpHit)
        if not na(tpslEntryLn)
            line.set_x2(tpslEntryLn, bar_index)
        if not na(tpslSlLn)
            line.set_x2(tpslSlLn, bar_index)
        if not na(tpslTp1Ln)
            line.set_x2(tpslTp1Ln, bar_index)
        if not na(tpslTp2Ln)
            line.set_x2(tpslTp2Ln, bar_index)
        if not na(tpslTp3Ln)
            line.set_x2(tpslTp3Ln, bar_index)
        if not na(tpslExtLn)
            line.set_x2(tpslExtLn, bar_index)
        tpslDir := 0

// Table

var table tb = table.new(tablePosition(tablePosIn), 2, 6, border_width = 1)
if showTable and barstate.islast
    color  tc = tblText
    string ts = tableTextSize(tableSizeIn)
    tb.set_bgcolor(color.new(tblBg, tblTransp))

    // Trend direction.
    string dirTxt = dir == 1 ? "▲ Uptrend" : "▼ Downtrend"

    // Trend age compared with the median duration of trends in the same direction.
    float  med    = dir == 1 ? durUp.median() : durDn.median()
    string ageTxt = str.tostring(age) + (age == 1 ? " bar" : " bars") + (na(med) ? "" : "  (median " + str.tostring(med, "0") + ")")

    // Weighted share of historical trends that lasted longer than the current one.
    string survTxt = nObs >= MIN_SAMPLES ? str.tostring(S * 100, "0") + "%" : "collecting data (" + str.tostring(nObs) + "/" + str.tostring(MIN_SAMPLES) + ")"

    // Move efficiency since the start of the trend. On a direction-change bar the new trend has no data yet.
    string erState = er >= erHi ? "directional" : er <= erLo ? "consolidation" : "moderate"
    string erTxt   = flipped ? "new trend" : str.tostring(er, "0.00") + "  " + erState

    // Current band multiplier.
    string bandState = mult >= baseMult * 0.97 ? "standard" : mult <= floorMult * 1.001 ? "minimum" : "tightened"
    string bandTxt   = str.tostring(mult, "0.00") + "× ATR  " + bandState

    // Stop level and its actual distance from the closing price.
    float  distPct = close != 0 ? math.abs(close - st) / close * 100 : na
    float  distAtr = atr > 0 ? math.abs(close - st) / atr : na
    string stopTxt = str.tostring(st, format.mintick) + "  (" + str.tostring(distPct, "0.00") + "% · " + str.tostring(distAtr, "0.0") + " ATR)"

    tb.cell(0, 0, "Trend",                 text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(1, 0, dirTxt,                  text_color = trendCol, text_size = ts, text_halign = text.align_left)
    tb.cell(0, 1, "Age",                   text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(1, 1, ageTxt,                  text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(0, 2, "Longer trends in history", text_color = tc,    text_size = ts, text_halign = text.align_left)
    tb.cell(1, 2, survTxt,                 text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(0, 3, "Efficiency since start", text_color = tc,      text_size = ts, text_halign = text.align_left)
    tb.cell(1, 3, erTxt,                   text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(0, 4, "Band multiplier",       text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(1, 4, bandTxt,                 text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(0, 5, "Stop",                  text_color = tc,       text_size = ts, text_halign = text.align_left)
    tb.cell(1, 5, stopTxt,                 text_color = trendCol, text_size = ts, text_halign = text.align_left)

// BACKTESTER OUTPUT PLOTS 
plot(buySignal ? 1 : na, "backtest_buy",  display = display.none)
plot(sellSignal ? 1 : na, "backtest_sell", display = display.none)

// Alerts
alertcondition(buySignal,  "Buy Signal",  "Age Adaptive Supertrend: Buy signal on {{ticker}} ({{interval}}) at {{close}}")
alertcondition(sellSignal, "Sell Signal", "Age Adaptive Supertrend: Sell signal on {{ticker}} ({{interval}}) at {{close}}")